package main

import (
	"fmt"
	"flag"
	"log"
	"sync"
	"regexp"
	"net/http"
	"crypto/md5"
	"com.rtsapp/httpserver"
)

const TypeAll   = 0
const TypeRosolved  = 1
const TypeNotRosolved  = 2

type ErrorInfo struct{
	stackMD5 string
	stack string
	dataCounts map[string]int
	resolved bool  
}

type ErrorDB struct{
	errors map[string]*ErrorInfo
	errorMutex sync.Mutex
}

func ( db *ErrorDB) resolveError( stackMD5 string , resolved bool){
	db.errorMutex.Lock()
	errorInfo, ok := db.errors[ stackMD5 ]
	if ok {
		errorInfo.resolved = resolved
	}
	db.errorMutex.Unlock()
}

func ( db *ErrorDB) postError( stack, data string ) {
	
	db.errorMutex.Lock()

	stackBytes := []byte( stack )
	stackMd5 := fmt.Sprintf("%x", md5.Sum(  stackBytes) )

	errorInfo, ok := db.errors[ stack ]
	if !ok {
		errorInfo = &ErrorInfo{ stackMD5 : stackMd5,  stack : stack, dataCounts : make( map[string]int ) , resolved : false }
		db.errors[  stackMd5 ] = errorInfo
	}

	count, ok := errorInfo.dataCounts[ data ]
	if !ok {
		errorInfo.dataCounts[ data ] = 1
	}else{
		errorInfo.dataCounts[ data ] = count + 1
	}

	db.errorMutex.Unlock()
}



func (db *ErrorDB) textError( listType int ) string{
	db.errorMutex.Lock()
	defer db.errorMutex.Unlock()

	pageText := "==========\r\n"
	lineText := "----------\r\n"
	newLine := "\r\n"


	regx , _ := regexp.Compile( "\n" )

	var page = ""
	for _, errorInfo := range db.errors {
		//如果只列出已解决的，没有解决的跳过
		if listType == TypeRosolved && !errorInfo.resolved {
			continue
		}
		if listType == TypeNotRosolved && errorInfo.resolved {
			continue
		}
		
		page += pageText

		page += regx.ReplaceAllString( errorInfo.stack, "\r\n" )
		page += lineText

		if errorInfo.resolved {
			page += "已解决\r\n"
		}else{
			page += "未解决\r\n"
		}
		page += lineText


		for error, count := range errorInfo.dataCounts {

			page += "出现次数:" + fmt.Sprintf("%d", count)
			page += newLine
			page += newLine
			page +=  regx.ReplaceAllString( error, "\r\n" ) 
			page += newLine
			page += lineText
		}

		page += newLine
	}

	return page
}


func (db *ErrorDB) listError( listType int ) string{
	db.errorMutex.Lock()
	defer db.errorMutex.Unlock()
	
	regx , _ := regexp.Compile( "\n" )


	var pagehead = `
	<html>
	<head><title>all errors </title></head>
	<body>
		`

	var pagebottom = `
	</body>
	</html>
	`

	var page = pagehead
	for stackMD5, errorInfo := range db.errors {
		//如果只列出已解决的，没有解决的跳过
		if listType == TypeRosolved && !errorInfo.resolved {
			continue
		}
		if listType == TypeNotRosolved && errorInfo.resolved {
			continue
		}

		page += `<div style="border-bottom:1px dashed #666666;margin: 0 0 5px 0;"> `

		page += ` <div>`
		page += regx.ReplaceAllString( errorInfo.stack, "<br/>" )
		page += `</div>`

		page += ` <div>`
		if errorInfo.resolved {
			page += `<form action="/error/notresolve" method="post" >
			<input type="hidden" name="stack" value="` + stackMD5 + `"/>
			<input type="submit" value="标记为未解决" />
			</form>`
		}else{
			page +=  `<form action="/error/resolve" method="post" >
			<input type="hidden" name="stack" value="` + stackMD5 + `"/>
			<input type="submit" value="标记为已解决" />
			</form>`
		}

		page += `</div>`


		page += `<ul>`
		for error, count := range errorInfo.dataCounts {
			page += `<li style="margin:0 0 5px 0;">`
			page += `<div>`
			page += "出现次数:" + fmt.Sprintf("%d", count)
			page += `</div>`
			page += `<div>`
			page += error
			page += `</div>`
			page += `</li>`
		}

		page += `</ul>`

		page += `</div>`
	}

	page += pagebottom

	return page
}

var errdb = &ErrorDB{ errors: make(map[string]*ErrorInfo) }

type PostErrorHandler struct{
}


func ( h *PostErrorHandler) ServeHTTP( w http.ResponseWriter, req *http.Request){

	fmt.Println( req.URL.Path )

	if req.URL.Path == "/error/post" {
		
		stack := req.PostFormValue( "stack" )
		data := req.PostFormValue( "data" )

		if  len( stack ) < 1   || len( data ) < 1   {
			fmt.Fprintf(w, " stack or data empty")
		}else{
			errdb.postError( stack, data )
		}

		fmt.Fprintf(w, "true")
	}else if req.URL.Path == "/error/listall" {
		fmt.Fprintf(w, errdb.listError( TypeAll ) )
	}else if req.URL.Path == "/error/list" {
		fmt.Fprintf(w, errdb.listError( TypeNotRosolved ) )
	}else if req.URL.Path == "/error/list1" {
		fmt.Fprintf(w, errdb.listError( TypeRosolved ) )
	}else if req.URL.Path == "/error/textall" {
		fmt.Fprintf(w, errdb.textError( TypeAll ) )
	}else if req.URL.Path == "/error/text" {
		fmt.Fprintf(w, errdb.textError( TypeNotRosolved ) )
	}else if req.URL.Path == "/error/text1" {
		fmt.Fprintf(w, errdb.textError( TypeRosolved ) )
	}else if req.URL.Path == "/error/resolve" {
		stack := req.PostFormValue( "stack" )
		if  len( stack ) < 1    {
			fmt.Fprintf(w, " stack  empty")
		}else{
			errdb.resolveError( stack , true )
		}
		fmt.Fprintf(w, "true" )
	}else if req.URL.Path == "/error/notresolve" {
		stack := req.PostFormValue( "stack" )
		if  len( stack ) < 1    {
			fmt.Fprintf(w, " stack  empty")
		}else{
			errdb.resolveError( stack , false )
		}
		fmt.Fprintf(w, "true" )
	}else{
		fmt.Println("other")
		fmt.Fprintf( w, "404" )
	}
	
}

var port *string = flag.String( "port", "7007", "listen port" )

func main() {
	
	flag.Parse()

	server := httpserver.NewHttpServer( *port )

	server.UrlMapping("/", &PostErrorHandler{} )

	log.Fatal( server.Start() )

	
}